{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "eec680f5",
   "metadata": {},
   "source": [
    "# Agent with Memory\n",
    "\n",
    "- Author: [Hye-yoon Jeong](https://github.com/Hye-yoonJeong)\n",
    "- Peer Review: \n",
    "- Proofread : [Chaeyoon Kim](https://github.com/chaeyoonyunakim)\n",
    "- This is a part of [LangChain Open Tutorial](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/17-LangGraph/01-Core-Features/04-LangGraph-Agent-With-Memory.ipynb)[![Open in GitHub](https://img.shields.io/badge/Open%20in%20GitHub-181717?style=flat-square&logo=github&logoColor=white)](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/17-LangGraph/01-Core-Features/04-LangGraph-Agent-With-Memory.ipynb)\n",
    "## Overview\n",
    "\n",
    "This tutorial covers how to add an **in-memory checkpoint saver** to an agent. \n",
    "\n",
    "An **in-memory checkpoint saver** enables an agent to store previous interactions, allowing the agent to engage in multi-turn conversations in a coherent manner.\n",
    "\n",
    "Also in this tutorial, we use ```ToolNode``` and ```tools_condition``` prebuilt in ```LangGraph``` instead of a customized tool node.\n",
    "\n",
    "### Table of Contents\n",
    "\n",
    "- [Overview](#overview)\n",
    "- [Environment Setup](#environment-setup)\n",
    "- [Memory Saver](#memory-saver)\n",
    "- [Configuration with RunnableConfig](#configuration-with-runnableconfig)\n",
    "- [Inspecting State Snapshots](#inspecting-state-snapshots)\n",
    "\n",
    "### References\n",
    "\n",
    "- [LangGraph: MemorySaver](https://langchain-ai.github.io/langgraph/reference/checkpoints/#langgraph.checkpoint.memory.MemorySaver)\n",
    "- [LangGraph: ToolNode](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.tool_node.ToolNode)\n",
    "- [LangGraph: tools_condition](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.tool_node.tools_condition)\n",
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e74d757d",
   "metadata": {},
   "source": [
    "## Environment Setup\n",
    "\n",
    "Setting up your environment is the first step. See the [Environment Setup guide](https://wikidocs.net/257836) for more details.\n",
    "\n",
    "**[Note]**\n",
    "\n",
    "```langchain-opentutorial``` is a package of easy-to-use environment setup guidance, useful functions and utilities for tutorials. Check out the [```langchain-opentutorial```](https://github.com/LangChain-OpenTutorial/langchain-opentutorial-pypi) for more details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9bb33a3b",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "[notice] A new release of pip is available: 24.1 -> 24.3.1\n",
      "[notice] To update, run: python.exe -m pip install --upgrade pip\n"
     ]
    }
   ],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install langchain-opentutorial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a3082f35",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install required packages\n",
    "from langchain_opentutorial import package\n",
    "\n",
    "package.install(\n",
    "    [\n",
    "        \"langsmith\",\n",
    "        \"langchain\",\n",
    "        \"langchain_core\",\n",
    "        \"langchain_community\",\n",
    "        \"langchain_openai\",\n",
    "        \"langgraph\",\n",
    "    ],\n",
    "    verbose=False,\n",
    "    upgrade=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ecf4dbd6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c84c6e6",
   "metadata": {},
   "source": [
    "You can set API keys in a .env file or set them manually.\n",
    "\n",
    "**[Note]** If you’re not using the .env file, no worries! Just enter the keys directly in the cell below, and you’re good to go."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4e1e8e13",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Environment variables have been set successfully.\n"
     ]
    }
   ],
   "source": [
    "# Set environment variables\n",
    "from langchain_opentutorial import set_env\n",
    "\n",
    "set_env(\n",
    "    {\n",
    "        # \"OPENAI_API_KEY\": \"\",\n",
    "        # \"LANGCHAIN_API_KEY\": \"\",\n",
    "        # \"TAVILY_API_KEY\": \"\",\n",
    "        \"LANGCHAIN_TRACING_V2\": \"true\",\n",
    "        \"LANGCHAIN_ENDPOINT\": \"https://api.smith.langchain.com\",\n",
    "        \"LANGCHAIN_PROJECT\": \"Agent-with-Memory\",\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6217bc3",
   "metadata": {},
   "source": [
    "## Memory Saver\n",
    "\n",
    "Without a memory to remember the **context**, an agent cannot engage in multi-turn interactions.\n",
    "\n",
    "In this tutorial, we use LangGraph's ```MemorySaver```, which stores checkpoints in memory.\n",
    "\n",
    "By providing a ```checkpointer``` during graph compilation and a ```thread_id``` when calling a graph, the state is **automatically saved** after each step. The ```thread_id``` acts as a key for continuous graph execution."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56bb0b0c",
   "metadata": {},
   "source": [
    "Steps to use ```MemorySaver```:\n",
    "1) Create a ```MemorySaver``` checkpointer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "53d80de1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "# Create a MemorySaver\n",
    "memory = MemorySaver()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7a7cd08",
   "metadata": {},
   "source": [
    "2) Define a graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "51549b1d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x1a49b269890>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_community.tools.tavily_search import TavilySearchResults\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "\n",
    "\n",
    "########## 1. Define state ##########\n",
    "# Define states to save after each step\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "\n",
    "########## 2. Define and bind tools ##########\n",
    "# Define tools\n",
    "tool = TavilySearchResults(k=3)\n",
    "tools = [tool]\n",
    "\n",
    "# Define LLM\n",
    "llm = ChatOpenAI(model=\"gpt-4o-mini\")\n",
    "\n",
    "# Bind LLM with tools\n",
    "llm_with_tools = llm.bind_tools(tools)\n",
    "\n",
    "\n",
    "########## 3. Define nodes ##########\n",
    "# Create a state graph\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "\n",
    "# Define and add a chatbot node\n",
    "def chatbot(state: State):\n",
    "    # Invoke and return messages\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "\n",
    "# Define and add a tool node\n",
    "tool_node = ToolNode(tools=[tool])\n",
    "graph_builder.add_node(\"tools\", tool_node)\n",
    "\n",
    "# Add a conditional edge\n",
    "graph_builder.add_conditional_edges(\n",
    "    \"chatbot\",\n",
    "    tools_condition,\n",
    ")\n",
    "\n",
    "########## 4. Add edges ##########\n",
    "# tools > chatbot\n",
    "graph_builder.add_edge(\"tools\", \"chatbot\")\n",
    "\n",
    "# START > chatbot\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "\n",
    "# chatbot > END\n",
    "graph_builder.add_edge(\"chatbot\", END)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c762780f",
   "metadata": {},
   "source": [
    "3) Compile the graph with the ```checkpointer```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "3d4ff857",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Compile a graph builder\n",
    "graph = graph_builder.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4271fd6",
   "metadata": {},
   "source": [
    "4) Visualize the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f55895f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import Image, display\n",
    "from langgraph.graph.state import CompiledStateGraph\n",
    "from dataclasses import dataclass\n",
    "\n",
    "\n",
    "# Define node styles to use for visualizing the graph\n",
    "@dataclass\n",
    "class NodeStyles:\n",
    "    default: str = (\n",
    "        \"fill:#45C4B0, fill-opacity:0.3, color:#23260F, stroke:#45C4B0, stroke-width:1px, font-weight:bold, line-height:1.2\"\n",
    "    )\n",
    "    first: str = (\n",
    "        \"fill:#45C4B0, fill-opacity:0.1, color:#23260F, stroke:#45C4B0, stroke-width:1px, font-weight:normal, font-style:italic, stroke-dasharray:2,2\"\n",
    "    )\n",
    "    last: str = (\n",
    "        \"fill:#45C4B0, fill-opacity:1, color:#000000, stroke:#45C4B0, stroke-width:1px, font-weight:normal, font-style:italic, stroke-dasharray:2,2\"\n",
    "    )\n",
    "\n",
    "\n",
    "# Define a function to visualize the graph\n",
    "def visualize_graph(graph, xray=False):\n",
    "    \"\"\"\n",
    "    Displays a visualization of the CompiledStateGraph object.\n",
    "\n",
    "    This function converts the given graph object,\n",
    "    if it is an instance of CompiledStateGraph, into a Mermaid-formatted PNG image and displays it.\n",
    "\n",
    "    Args:\n",
    "        graph: The graph object to be visualized. Must be an instance of CompiledStateGraph.\n",
    "\n",
    "    Returns:\n",
    "        None\n",
    "\n",
    "    Raises:\n",
    "        Exception: Raised if an error occurs during the graph visualization process.\n",
    "    \"\"\"\n",
    "    try:\n",
    "        # Visualize the graph\n",
    "        if isinstance(graph, CompiledStateGraph):\n",
    "            display(\n",
    "                Image(\n",
    "                    graph.get_graph(xray=xray).draw_mermaid_png(\n",
    "                        background_color=\"white\",\n",
    "                        node_colors=NodeStyles(),\n",
    "                    )\n",
    "                )\n",
    "            )\n",
    "    except Exception as e:\n",
    "        print(f\"[ERROR] Visualize Graph Error: {e}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8928997e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "visualize_graph(graph)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e32dbc5",
   "metadata": {},
   "source": [
    "## Configuration with ```RunnableConfig```\n",
    "\n",
    "Define a ```RunnableConfig``` object and set the ```recursion_limit``` and ```thread_id``` properties.\n",
    "\n",
    "- ```recursion_limit```: The maximum number of nodes the graph can visit. Exceeding this limit will trigger a ```RecursionError```.\n",
    "- ```thread_id```: An ID to distinguish different sessions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2fea0653",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnableConfig\n",
    "\n",
    "config = RunnableConfig(\n",
    "    recursion_limit=10,\n",
    "    configurable={\"thread_id\": \"1\"},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ab5c0bad",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hi Hannah! Nice to meet you too! That's awesome that you run a YouTube channel. What kind of content do you create?\n"
     ]
    }
   ],
   "source": [
    "# First question for {\"thread_id\": \"1\"}\n",
    "question = \"Hi! My name is Hannah. I run a YouTube channel. Nice to meet you!\"\n",
    "\n",
    "for event in graph.stream({\"messages\": [(\"user\", question)]}, config=config):\n",
    "    for value in event.values():\n",
    "        value[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4bac57c9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Yes, your name is Hannah! How can I assist you today?\n"
     ]
    }
   ],
   "source": [
    "# Second question for {\"thread_id\": \"1\"}\n",
    "question = \"Do you remember my name?\"\n",
    "\n",
    "for event in graph.stream({\"messages\": [(\"user\", question)]}, config=config):\n",
    "    for value in event.values():\n",
    "        value[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "574988ab",
   "metadata": {},
   "source": [
    "Modify the ```thread_id``` in the ```RunnableConfig``` to see if the agent remembers the previous conversation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "e958ba53",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = RunnableConfig(\n",
    "    recursion_limit=10,\n",
    "    configurable={\"thread_id\": \"2\"},  # Change the thread_id\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "68ce68f9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I don't have the ability to remember personal information or previous interactions. Each session is treated independently. How can I assist you today?\n"
     ]
    }
   ],
   "source": [
    "question = \"Do you remember my name?\"\n",
    "\n",
    "for event in graph.stream({\"messages\": [(\"user\", question)]}, config=config):\n",
    "    for value in event.values():\n",
    "        value[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "857821c4",
   "metadata": {},
   "source": [
    "## Inspecting State Snapshots\n",
    "\n",
    "A checkpoint (```snapshot```) stores:\n",
    " - the current state values\n",
    " - corresponding config\n",
    " - The **next** node to process (empty at the end).\n",
    "\n",
    "Call ```get_state(config)``` to see a graph's ```state``` for a certain config."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "16d9c636",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = RunnableConfig(\n",
    "    configurable={\"thread_id\": \"1\"},\n",
    ")\n",
    "# Create a snapshot of the graph state\n",
    "snapshot = graph.get_state(config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "5c9238d8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "StateSnapshot(values={'messages': [HumanMessage(content='Hi! My name is Hannah. I run a YouTube channel. Nice to meet you!', additional_kwargs={}, response_metadata={}, id='f97ccf7d-a15d-4b02-9974-3908b008e181'), AIMessage(content=\"Hi Hannah! Nice to meet you too! That's awesome that you run a YouTube channel. What kind of content do you create?\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 29, 'prompt_tokens': 98, 'total_tokens': 127, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-4a48ff7d-4c42-4406-a65c-047e55d782bb-0', usage_metadata={'input_tokens': 98, 'output_tokens': 29, 'total_tokens': 127, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), HumanMessage(content='Do you remember my name?', additional_kwargs={}, response_metadata={}, id='05bcbd52-3ce8-408e-9ba4-2087d46d8f9a'), AIMessage(content='Yes, your name is Hannah! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 139, 'total_tokens': 155, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-13f47663-d83d-4d9d-9304-acbabb32e5fe-0', usage_metadata={'input_tokens': 139, 'output_tokens': 16, 'total_tokens': 155, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efd6f6a-bfa9-6ae9-8004-08c865005c63'}}, metadata={'source': 'loop', 'writes': {'chatbot': {'messages': [AIMessage(content='Yes, your name is Hannah! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 139, 'total_tokens': 155, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-13f47663-d83d-4d9d-9304-acbabb32e5fe-0', usage_metadata={'input_tokens': 139, 'output_tokens': 16, 'total_tokens': 155, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}, 'thread_id': '1', 'step': 4, 'parents': {}}, created_at='2025-01-20T06:20:40.740119+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efd6f6a-b6e5-62c6-8003-cf6983af2d22'}}, tasks=())"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "snapshot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ef39bd5",
   "metadata": {},
   "source": [
    "You can access the **configuration** with ```snapshot.config```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "a203fa62",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '1',\n",
       "  'checkpoint_ns': '',\n",
       "  'checkpoint_id': '1efd6f6a-bfa9-6ae9-8004-08c865005c63'}}"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Configuration of the snapshot\n",
    "snapshot.config"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4917d010",
   "metadata": {},
   "source": [
    "You can access the **saved states** with ```snapshot.value```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "683a655b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='Hi! My name is Hannah. I run a YouTube channel. Nice to meet you!', additional_kwargs={}, response_metadata={}, id='f97ccf7d-a15d-4b02-9974-3908b008e181'),\n",
       "  AIMessage(content=\"Hi Hannah! Nice to meet you too! That's awesome that you run a YouTube channel. What kind of content do you create?\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 29, 'prompt_tokens': 98, 'total_tokens': 127, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-4a48ff7d-4c42-4406-a65c-047e55d782bb-0', usage_metadata={'input_tokens': 98, 'output_tokens': 29, 'total_tokens': 127, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n",
       "  HumanMessage(content='Do you remember my name?', additional_kwargs={}, response_metadata={}, id='05bcbd52-3ce8-408e-9ba4-2087d46d8f9a'),\n",
       "  AIMessage(content='Yes, your name is Hannah! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 139, 'total_tokens': 155, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-13f47663-d83d-4d9d-9304-acbabb32e5fe-0', usage_metadata={'input_tokens': 139, 'output_tokens': 16, 'total_tokens': 155, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Values saved in the snapshot\n",
    "snapshot.values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6223078c",
   "metadata": {},
   "source": [
    "You can use ```snapshot.next``` to see which **node** will be processed next. In this case, ```snapshot.next``` is empty since the graph reaches the **END** node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "c38af74d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Next node\n",
    "snapshot.next"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "feb4adfc",
   "metadata": {},
   "source": [
    "Define custom functions to view the complete contents of ```snapshot``` and its metadata (```snapshot.metadata```)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "7628dd91",
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "\n",
    "# Custom function to serialize non-JSON serializable objects\n",
    "def custom_serializer(obj):\n",
    "    if hasattr(obj, \"__dict__\"):\n",
    "        return obj.__dict__\n",
    "    elif isinstance(obj, (list, tuple)):\n",
    "        return [custom_serializer(item) for item in obj]\n",
    "    return str(obj)\n",
    "\n",
    "\n",
    "# Custom function to print the snapshot\n",
    "def pretty_print_snapshot(snapshot):\n",
    "    try:\n",
    "        snapshot_json = json.dumps(\n",
    "            snapshot, indent=4, ensure_ascii=False, default=custom_serializer\n",
    "        )\n",
    "        print(snapshot_json)\n",
    "    except Exception as e:\n",
    "        print(f\"Error formatting snapshot: {e}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "d9192714",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "    {\n",
      "        \"messages\": [\n",
      "            {\n",
      "                \"content\": \"Hi! My name is Hannah. I run a YouTube channel. Nice to meet you!\",\n",
      "                \"additional_kwargs\": {},\n",
      "                \"response_metadata\": {},\n",
      "                \"type\": \"human\",\n",
      "                \"name\": null,\n",
      "                \"id\": \"f97ccf7d-a15d-4b02-9974-3908b008e181\",\n",
      "                \"example\": false\n",
      "            },\n",
      "            {\n",
      "                \"content\": \"Hi Hannah! Nice to meet you too! That's awesome that you run a YouTube channel. What kind of content do you create?\",\n",
      "                \"additional_kwargs\": {\n",
      "                    \"refusal\": null\n",
      "                },\n",
      "                \"response_metadata\": {\n",
      "                    \"token_usage\": {\n",
      "                        \"completion_tokens\": 29,\n",
      "                        \"prompt_tokens\": 98,\n",
      "                        \"total_tokens\": 127,\n",
      "                        \"completion_tokens_details\": {\n",
      "                            \"accepted_prediction_tokens\": 0,\n",
      "                            \"audio_tokens\": 0,\n",
      "                            \"reasoning_tokens\": 0,\n",
      "                            \"rejected_prediction_tokens\": 0\n",
      "                        },\n",
      "                        \"prompt_tokens_details\": {\n",
      "                            \"audio_tokens\": 0,\n",
      "                            \"cached_tokens\": 0\n",
      "                        }\n",
      "                    },\n",
      "                    \"model_name\": \"gpt-4o-mini-2024-07-18\",\n",
      "                    \"system_fingerprint\": \"fp_72ed7ab54c\",\n",
      "                    \"finish_reason\": \"stop\",\n",
      "                    \"logprobs\": null\n",
      "                },\n",
      "                \"type\": \"ai\",\n",
      "                \"name\": null,\n",
      "                \"id\": \"run-4a48ff7d-4c42-4406-a65c-047e55d782bb-0\",\n",
      "                \"example\": false,\n",
      "                \"tool_calls\": [],\n",
      "                \"invalid_tool_calls\": [],\n",
      "                \"usage_metadata\": {\n",
      "                    \"input_tokens\": 98,\n",
      "                    \"output_tokens\": 29,\n",
      "                    \"total_tokens\": 127,\n",
      "                    \"input_token_details\": {\n",
      "                        \"audio\": 0,\n",
      "                        \"cache_read\": 0\n",
      "                    },\n",
      "                    \"output_token_details\": {\n",
      "                        \"audio\": 0,\n",
      "                        \"reasoning\": 0\n",
      "                    }\n",
      "                }\n",
      "            },\n",
      "            {\n",
      "                \"content\": \"Do you remember my name?\",\n",
      "                \"additional_kwargs\": {},\n",
      "                \"response_metadata\": {},\n",
      "                \"type\": \"human\",\n",
      "                \"name\": null,\n",
      "                \"id\": \"05bcbd52-3ce8-408e-9ba4-2087d46d8f9a\",\n",
      "                \"example\": false\n",
      "            },\n",
      "            {\n",
      "                \"content\": \"Yes, your name is Hannah! How can I assist you today?\",\n",
      "                \"additional_kwargs\": {\n",
      "                    \"refusal\": null\n",
      "                },\n",
      "                \"response_metadata\": {\n",
      "                    \"token_usage\": {\n",
      "                        \"completion_tokens\": 16,\n",
      "                        \"prompt_tokens\": 139,\n",
      "                        \"total_tokens\": 155,\n",
      "                        \"completion_tokens_details\": {\n",
      "                            \"accepted_prediction_tokens\": 0,\n",
      "                            \"audio_tokens\": 0,\n",
      "                            \"reasoning_tokens\": 0,\n",
      "                            \"rejected_prediction_tokens\": 0\n",
      "                        },\n",
      "                        \"prompt_tokens_details\": {\n",
      "                            \"audio_tokens\": 0,\n",
      "                            \"cached_tokens\": 0\n",
      "                        }\n",
      "                    },\n",
      "                    \"model_name\": \"gpt-4o-mini-2024-07-18\",\n",
      "                    \"system_fingerprint\": \"fp_72ed7ab54c\",\n",
      "                    \"finish_reason\": \"stop\",\n",
      "                    \"logprobs\": null\n",
      "                },\n",
      "                \"type\": \"ai\",\n",
      "                \"name\": null,\n",
      "                \"id\": \"run-13f47663-d83d-4d9d-9304-acbabb32e5fe-0\",\n",
      "                \"example\": false,\n",
      "                \"tool_calls\": [],\n",
      "                \"invalid_tool_calls\": [],\n",
      "                \"usage_metadata\": {\n",
      "                    \"input_tokens\": 139,\n",
      "                    \"output_tokens\": 16,\n",
      "                    \"total_tokens\": 155,\n",
      "                    \"input_token_details\": {\n",
      "                        \"audio\": 0,\n",
      "                        \"cache_read\": 0\n",
      "                    },\n",
      "                    \"output_token_details\": {\n",
      "                        \"audio\": 0,\n",
      "                        \"reasoning\": 0\n",
      "                    }\n",
      "                }\n",
      "            }\n",
      "        ]\n",
      "    },\n",
      "    [],\n",
      "    {\n",
      "        \"configurable\": {\n",
      "            \"thread_id\": \"1\",\n",
      "            \"checkpoint_ns\": \"\",\n",
      "            \"checkpoint_id\": \"1efd6f6a-bfa9-6ae9-8004-08c865005c63\"\n",
      "        }\n",
      "    },\n",
      "    {\n",
      "        \"source\": \"loop\",\n",
      "        \"writes\": {\n",
      "            \"chatbot\": {\n",
      "                \"messages\": [\n",
      "                    {\n",
      "                        \"content\": \"Yes, your name is Hannah! How can I assist you today?\",\n",
      "                        \"additional_kwargs\": {\n",
      "                            \"refusal\": null\n",
      "                        },\n",
      "                        \"response_metadata\": {\n",
      "                            \"token_usage\": {\n",
      "                                \"completion_tokens\": 16,\n",
      "                                \"prompt_tokens\": 139,\n",
      "                                \"total_tokens\": 155,\n",
      "                                \"completion_tokens_details\": {\n",
      "                                    \"accepted_prediction_tokens\": 0,\n",
      "                                    \"audio_tokens\": 0,\n",
      "                                    \"reasoning_tokens\": 0,\n",
      "                                    \"rejected_prediction_tokens\": 0\n",
      "                                },\n",
      "                                \"prompt_tokens_details\": {\n",
      "                                    \"audio_tokens\": 0,\n",
      "                                    \"cached_tokens\": 0\n",
      "                                }\n",
      "                            },\n",
      "                            \"model_name\": \"gpt-4o-mini-2024-07-18\",\n",
      "                            \"system_fingerprint\": \"fp_72ed7ab54c\",\n",
      "                            \"finish_reason\": \"stop\",\n",
      "                            \"logprobs\": null\n",
      "                        },\n",
      "                        \"type\": \"ai\",\n",
      "                        \"name\": null,\n",
      "                        \"id\": \"run-13f47663-d83d-4d9d-9304-acbabb32e5fe-0\",\n",
      "                        \"example\": false,\n",
      "                        \"tool_calls\": [],\n",
      "                        \"invalid_tool_calls\": [],\n",
      "                        \"usage_metadata\": {\n",
      "                            \"input_tokens\": 139,\n",
      "                            \"output_tokens\": 16,\n",
      "                            \"total_tokens\": 155,\n",
      "                            \"input_token_details\": {\n",
      "                                \"audio\": 0,\n",
      "                                \"cache_read\": 0\n",
      "                            },\n",
      "                            \"output_token_details\": {\n",
      "                                \"audio\": 0,\n",
      "                                \"reasoning\": 0\n",
      "                            }\n",
      "                        }\n",
      "                    }\n",
      "                ]\n",
      "            }\n",
      "        },\n",
      "        \"thread_id\": \"1\",\n",
      "        \"step\": 4,\n",
      "        \"parents\": {}\n",
      "    },\n",
      "    \"2025-01-20T06:20:40.740119+00:00\",\n",
      "    {\n",
      "        \"configurable\": {\n",
      "            \"thread_id\": \"1\",\n",
      "            \"checkpoint_ns\": \"\",\n",
      "            \"checkpoint_id\": \"1efd6f6a-b6e5-62c6-8003-cf6983af2d22\"\n",
      "        }\n",
      "    },\n",
      "    []\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "pretty_print_snapshot(snapshot)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "294ff7b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "    \"source\": \"loop\",\n",
      "    \"writes\": {\n",
      "        \"chatbot\": {\n",
      "            \"messages\": [\n",
      "                {\n",
      "                    \"content\": \"Yes, your name is Hannah! How can I assist you today?\",\n",
      "                    \"additional_kwargs\": {\n",
      "                        \"refusal\": null\n",
      "                    },\n",
      "                    \"response_metadata\": {\n",
      "                        \"token_usage\": {\n",
      "                            \"completion_tokens\": 16,\n",
      "                            \"prompt_tokens\": 139,\n",
      "                            \"total_tokens\": 155,\n",
      "                            \"completion_tokens_details\": {\n",
      "                                \"accepted_prediction_tokens\": 0,\n",
      "                                \"audio_tokens\": 0,\n",
      "                                \"reasoning_tokens\": 0,\n",
      "                                \"rejected_prediction_tokens\": 0\n",
      "                            },\n",
      "                            \"prompt_tokens_details\": {\n",
      "                                \"audio_tokens\": 0,\n",
      "                                \"cached_tokens\": 0\n",
      "                            }\n",
      "                        },\n",
      "                        \"model_name\": \"gpt-4o-mini-2024-07-18\",\n",
      "                        \"system_fingerprint\": \"fp_72ed7ab54c\",\n",
      "                        \"finish_reason\": \"stop\",\n",
      "                        \"logprobs\": null\n",
      "                    },\n",
      "                    \"type\": \"ai\",\n",
      "                    \"name\": null,\n",
      "                    \"id\": \"run-13f47663-d83d-4d9d-9304-acbabb32e5fe-0\",\n",
      "                    \"example\": false,\n",
      "                    \"tool_calls\": [],\n",
      "                    \"invalid_tool_calls\": [],\n",
      "                    \"usage_metadata\": {\n",
      "                        \"input_tokens\": 139,\n",
      "                        \"output_tokens\": 16,\n",
      "                        \"total_tokens\": 155,\n",
      "                        \"input_token_details\": {\n",
      "                            \"audio\": 0,\n",
      "                            \"cache_read\": 0\n",
      "                        },\n",
      "                        \"output_token_details\": {\n",
      "                            \"audio\": 0,\n",
      "                            \"reasoning\": 0\n",
      "                        }\n",
      "                    }\n",
      "                }\n",
      "            ]\n",
      "        }\n",
      "    },\n",
      "    \"thread_id\": \"1\",\n",
      "    \"step\": 4,\n",
      "    \"parents\": {}\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "pretty_print_snapshot(snapshot.metadata)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-ARohChI8-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
